Playwrightテストの実行順序制御:playwright.config.tsとシリアル/パラレル実行の組み合わせ

Playwrightテストの実行順序制御:playwright.config.tsとシリアル/パラレル実行の組み合わせ

Playwrightのテスト実行順序を、設定ファイルとテストファイル内の制御で最適化する方法を解説します。参照系の並列実行による効率化と、データ更新テストの適切な配置で、信頼性の高いテスト実行を実現しましょう。
Clock Icon2024.12.22

情報システム室の進地@日比谷です。

Playwrightでのテスト実行において、テストケース間の実行順序の制御は重要な課題の一つです。この記事では、設定ファイルとテストファイル内での制御方法を組み合わせた実践的なアプローチをご紹介します。

はじめに

テスト実行順序の制御が必要となるケース

1. 画面操作の逐次実行が必要な場合

ユーザーの一連の操作フローをテストする際、画面遷移や状態変更を順序通りに検証する必要があります。例えば

  • ユーザー登録→ログイン→プロフィール編集といった一連の操作フロー
  • 商品登録→在庫確認→価格変更といった管理画面での操作シーケンス

これらのケースでは、前のテストの結果が次のテストの前提条件となるため、直列実行(シリアル実行)が必須となります。

2. データ更新を伴うテストケースの制御

テストデータを更新する操作を含むテストケースでは、以下の点に注意が必要です。

  • データの更新→テストの実行→データの復元という一連の流れを、途中で中断されることなく実行する必要がある
  • 他のテストケースによるデータ参照と更新が競合するのを防ぐ必要がある
  • データの整合性を保つため、更新系のテストは他のテストと分離して実行する

3. 参照系テストケースの効率化

一方で、データの更新を伴わないテストケース(例:検索機能や一覧表示のテストなど)は、互いに影響を与えることなく実行できます。

  • 複数のテストケースを並列実行することで、全体の実行時間を短縮
  • リソースの効率的な活用が可能
  • テスト実行時間を大幅に短縮でき、開発サイクルを速められる

Playwrightでの制御アプローチ

Playwrightでは、以下の2つのアプローチでテストの実行順序を制御できます。

playwright.config.tsによる静的な制御

設定ファイルを使用して

  • テストファイルの実行順序を制御
  • 特定のテストを特定のタイミングで実行するよう設定
  • テストのグルーピングと実行順序の管理

test.describe.serial/parallelによる動的な制御

テストコード内で

  • テストケースの直列実行/並列実行を柔軟に切り替え
  • テストケースのグループ化と実行モードの指定
  • 状況に応じた実行制御の実装

playwright.config.tsによる実行順序の制御

Playwrightの設定ファイルを使用することで、テストファイルの実行順序を制御できます。ここでは、実際のユースケースに基づいた設定方法を見ていきましょう。

基本的な設定方法

playwright.config.tsでは、testDir、testMatch、testIgnoreなどのオプションを使ってテストファイルの実行を制御できます。

import { PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {
  testDir: './tests',
  testMatch: [
    // 認証関連のテストを最初に実行
    'auth/**/*.spec.ts',
    // 参照系のテストを次に実行
    'reference/**/*.spec.ts',
    // 最後にデータ更新を伴うテストを実行
    'data-modification/**/*.spec.ts'
  ],
  // 特定のテストを除外
  testIgnore: ['**/ignored/**/*.spec.ts']
};

export default config;

フォルダ構造を活用した制御

テストの特性に応じて、以下のようなフォルダ構造を採用することで、実行順序の管理がしやすくなります。

tests/
├── 1-auth/           # 認証関連のテスト(最初に実行)
├── 2-reference/      # 参照系のテスト(並列実行可能)
└── 3-data-update/    # データ更新を伴うテスト(最後に実行)

この構造に対応する設定例は次の通りです。

const config: PlaywrightTestConfig = {
  testDir: './tests',
  testMatch: [
    '1-auth/**/*.spec.ts',
    '2-reference/**/*.spec.ts',
    '3-data-update/**/*.spec.ts'
  ],
  // フォルダごとに異なる実行設定を適用
  projects: [
    {
      name: 'auth',
      testMatch: '1-auth/**/*.spec.ts',
      workers: 1  // 認証テストは1つずつ実行
    },
    {
      name: 'reference',
      testMatch: '2-reference/**/*.spec.ts',
      workers: 3  // 参照系テストは並列実行
    },
    {
      name: 'data-update',
      testMatch: '3-data-update/**/*.spec.ts',
      workers: 1  // データ更新テストは1つずつ実行
    }
  ]
};

テストファイル内での実行モード制御

基本的な実行モードの設定

テストファイルの冒頭で、そのファイル内のテストの基本実行モードを設定できます。

import { test } from '@playwright/test';

// ファイル内のテストを基本的に並列実行するよう設定
test.describe.configure({ mode: 'parallel' });

test.describe.serialの使用方法

基本的な並列実行の中で、直列実行が必要なテストケースのみをグループ化する場合に使用します。

import { test } from '@playwright/test';

// ファイル内のテストを基本的に並列実行するよう設定
test.describe.configure({ mode: 'parallel' });

// 商品データの更新に関するテストは直列実行が必要
test.describe.serial('商品データの更新テスト', () => {
  test('商品を新規登録する', async ({ page }) => {
    // 商品登録の処理
  });

  test('登録した商品の内容を編集する', async ({ page }) => {
    // 商品編集の処理
  });

  test('商品を削除する', async ({ page }) => {
    // 商品削除の処理
  });
});

test.describe.parallelの使用方法

基本設定で並列実行を指定している場合、test.describe.parallelの指定は冗長になりますが、
コードの意図を明確にするために記述することもあります。

import { test } from '@playwright/test';

// ファイル内のテストを基本的に並列実行するよう設定
test.describe.configure({ mode: 'parallel' });

// 商品検索に関するテスト(明示的に並列実行を指定)
test.describe.parallel('商品検索のテスト', () => {
  test('キーワードで商品を検索できる', async ({ page }) => {
    // キーワード検索のテスト
  });

  test('カテゴリで商品を絞り込める', async ({ page }) => {
    // カテゴリ検索のテスト
  });

  test('価格帯で商品を絞り込める', async ({ page }) => {
    // 価格帯による絞り込みのテスト
  });
});

実行モードの組み合わせ

同じファイル内で直列実行と並列実行を組み合わせる例。

import { test } from '@playwright/test';

// ファイル内のテストを基本的に並列実行するよう設定
test.describe.configure({ mode: 'parallel' });

// 商品管理の一連のテスト
test.describe('商品管理テスト', () => {
  // データ更新を伴うテストは明示的に直列実行を指定
  test.describe.serial('商品データの操作', () => {
    test('商品を登録する', async ({ page }) => {
      // 商品登録処理
    });

    test('商品を更新する', async ({ page }) => {
      // 商品更新処理
    });
  });

  // 基本設定が並列実行なので、以下のブロックは並列実行される
  test.describe('商品データの参照', () => {
    test('商品一覧を表示できる', async ({ page }) => {
      // 一覧表示のテスト
    });

    test('商品の詳細を表示できる', async ({ page }) => {
      // 詳細表示のテスト
    });
  });
});

実践的な組み合わせパターン

これまでの内容を組み合わせた、実践的な実装例を見ていきましょう。ここでは商品管理システムのテストを例に、設定ファイルとテストファイル内の制御を組み合わせる方法を説明します。

プロジェクト構造

tests/
├── 1-auth/
│   └── login.spec.ts
├── 2-reference/
│   └── product-search.spec.ts  # 商品検索機能のテスト
└── 3-data-update/
    └── product-crud.spec.ts    # 商品のCRUD操作テスト

設定ファイル

// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {
  testDir: './tests',
  testMatch: [
    '1-auth/**/*.spec.ts',
    '2-reference/**/*.spec.ts',
    '3-data-update/**/*.spec.ts'
  ],
  projects: [
    {
      name: 'auth',
      testMatch: '1-auth/**/*.spec.ts',
      workers: 1
    },
    {
      name: 'reference',
      testMatch: '2-reference/**/*.spec.ts',
      workers: 3
    },
    {
      name: 'data-update',
      testMatch: '3-data-update/**/*.spec.ts',
      workers: 1
    }
  ]
};

export default config;

テストファイルの実装

認証テスト

// login.spec.ts
import { test } from '@playwright/test';

// 認証関連のテストは直列実行
test.describe.serial('認証テスト', () => {
  test('ログインができる', async ({ page }) => {
    // ログインのテスト実装
  });

  test('ログアウトができる', async ({ page }) => {
    // ログアウトのテスト実装
  });
});

商品検索のテスト

// product-search.spec.ts
import { test } from '@playwright/test';

// ファイル内のテストを基本的に並列実行に設定
test.describe.configure({ mode: 'parallel' });

// 検索機能のテストは互いに独立しているので並列実行可能
test.describe('商品検索機能', () => {
  test('キーワードで検索できる', async ({ page }) => {
    // キーワード検索のテスト実装
  });

  test('カテゴリで絞り込める', async ({ page }) => {
    // カテゴリ検索のテスト実装
  });

  test('価格帯で絞り込める', async ({ page }) => {
    // 価格帯検索のテスト実装
  });
});

商品CRUD操作のテスト

// product-crud.spec.ts
import { test } from '@playwright/test';

// データ更新を伴うテストは直列実行が必要
test.describe.serial('商品データの更新', () => {
  test('商品を新規登録できる', async ({ page }) => {
    // 商品登録のテスト実装
  });

  test('商品情報を編集できる', async ({ page }) => {
    // 商品編集のテスト実装
  });

  test('商品を削除できる', async ({ page }) => {
    // 商品削除のテスト実装
  });
});

実行順序の制御の仕組み

  1. まずplaywright.config.tsの設定により

    • 認証関連のテスト(1-auth/配下)が最初に実行される
    • 続いて参照系のテスト(2-reference/配下)が並列実行される
    • 最後にデータ更新のテスト(3-data-update/配下)が実行される
    • workersの設定により、認証テストとデータ更新テストは1つずつ、参照系テストは最大3つまで並列実行
  2. 各テストファイル内では

    • login.spec.tstest.describe.serialにより、ログイン→ログアウトの順で実行
    • product-search.spec.tsは並列実行設定により、検索機能のテストを同時実行
    • product-crud.spec.tstest.describe.serialで商品のCRUD操作を直列実行

注意点とベストプラクティス

テスト失敗時のデータ整合性の確保

テストの実行順序を制御できたとしても、テストは常に失敗する可能性があります。特にデータを更新するテストが失敗した場合、後続のテストに影響を与える可能性があります。

データ更新テストの配置

  • データを更新するテストは、可能な限り最後に実行されるよう配置します。
    tests/
    ├── 1-auth/          # 認証関連のテスト(最初に実行)
    ├── 2-reference/     # 参照系のテスト(並列実行)
    └── 3-data-update/   # データ更新系のテスト(最後に実行)
    

テストデータの初期化

テストの信頼性を確保するために、テスト実行前にデータを既知の状態に戻すことが重要です。

  • 定期的なデータリフレッシュの仕組みを用意する
    • 例:Salesforceのテスト環境(Sandbox)にて、テスト実行の半日前にRefreshをかけて、データを初期状態に戻す
  • テスト実行前のデータ初期化処理を実装する

まとめ

本記事では、Playwrightのテスト実行順序を制御する方法として

  • playwright.config.tsによるファイルレベルでの制御
  • test.describe.serial/parallelによるテストケースレベルでの制御
  • フォルダ構造を活用した実行順序の管理

について説明しました。

特に重要なポイントとして

  • 参照系テストは並列実行で効率化
  • データ更新系テストは最後に実行
  • テストデータの初期化を確実に行う

これらの方法を適切に組み合わせることで、テストの信頼性を保ちながら効率的な実行を実現できます。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.